package com.tbgov.util;

import com.tbgov.enums.FileUnitEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 基础工具类
 *
 * @author 1st
 */
public class BaseUtil {

    /**
     * 该函数用于复制一个对象的属性到另一个新创建的对象中
     *
     * @param source 源对象
     * @param clazz  目标对象类型
     * @param <T>    目标对象类型
     * @return 返回新创建的对象
     */
    public static <T> T copyProperties(Object source, Class<T> clazz) {
        // 确保源对象不为空
        Assert.isTrue(Objects.nonNull(source), "源对象不能为空！");
        try {
            // 创建目标对象实例
            T t = clazz.getDeclaredConstructor().newInstance();
            // 复制源对象的属性到目标对象
            BeanUtils.copyProperties(source, t);
            // 返回新创建的目标对象
            return t;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            // 输出异常信息
            // e.printStackTrace();
        }
        // 如果对象创建失败，抛出运行时异常
        throw new RuntimeException("对象创建失败！");
    }

    /**
     * 判断字符串(分隔符分割的数组)是否为数值
     *
     * @param str       要判断的字符串值
     * @param separator 是否存在分隔符
     * @return boolean
     */
    public static boolean isNumeric(String str, String separator) {
        if (Objects.isNull(str)) {
            return false;
        }
        Function<String, Boolean> fun = (s) -> Pattern.compile("-?[0-9]+(\\.[0-9]+)?").matcher(s).matches();
        if (Objects.nonNull(separator)) {
            return Arrays.stream(str.split(separator)).allMatch(fun::apply);
        }
        return fun.apply(str);
    }

    public static boolean isNumeric(String str) {
        return isNumeric(str, null);
    }


    public static double unitConversion(Long size) {
        return unitConversion(size, FileUnitEnum.BYTES, FileUnitEnum.MB);
    }

    /**
     * 文件大小单位的转换
     *
     * @param size        文件大小字节
     * @param sourceUnit  源文件单单位枚举
     * @param convertUnit 转换单位枚举
     * @return double
     */
    public static double unitConversion(Long size, FileUnitEnum sourceUnit, FileUnitEnum convertUnit) {
        Integer convert = convertUnit.getCode(), source = sourceUnit.getCode();
        if (FileUnitEnum.BYTES.getCode().equals(source)) {
            if (FileUnitEnum.BIT.getCode().equals(convert)) {
                return (double) size * 8;
            } else if (FileUnitEnum.KB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 1);
            } else if (FileUnitEnum.MB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 2);
            } else if (FileUnitEnum.GB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 3);
            }
        } else if (FileUnitEnum.KB.getCode().equals(source)) {
            if (FileUnitEnum.BIT.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 1) * 8;
            } else if (FileUnitEnum.BYTES.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 1);
            } else if (FileUnitEnum.MB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 1);
            } else if (FileUnitEnum.GB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 2);
            }
        } else if (FileUnitEnum.MB.getCode().equals(source)) {
            if (FileUnitEnum.BIT.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 2) * 8;
            } else if (FileUnitEnum.BYTES.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 2);
            } else if (FileUnitEnum.KB.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 1);
            } else if (FileUnitEnum.GB.getCode().equals(convert)) {
                return (double) size / Math.pow(1024, 1);
            }
        } else if (FileUnitEnum.GB.getCode().equals(source)) {
            if (FileUnitEnum.BIT.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 3) * 8;
            } else if (FileUnitEnum.BYTES.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 3);
            } else if (FileUnitEnum.KB.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 2);
            } else if (FileUnitEnum.MB.getCode().equals(convert)) {
                return (double) size * Math.pow(1024, 1);
            }
        }
        return size;
    }

    /**
     * 获取在线链接后缀文件类型
     *
     * @param link 链接
     * @return String
     */
    public static String queryPostfix(String link, String separator) {
        Assert.isTrue(Objects.nonNull(link), "链接不能为空！");
        Optional.ofNullable(separator).orElseThrow(() -> new NullPointerException("分割符不能空！"));
        String[] split = link.split(separator);
        Assert.isTrue(split.length > 1, "链接无包含该分割符，无法分割获取！");
        return split[split.length - 1];
    }

    public static String queryPostfix(String link) {
        return queryPostfix(link, "\\.");
    }

    /**
     * 创建唯一文件名
     *
     * @param companyId 公司id
     * @param userId    用户id
     * @return String
     */
    public static String createUniqueFileName(Long companyId, Long userId) {
        StringBuilder prefix = new StringBuilder(System.currentTimeMillis() + "");
        // 链接上存用户id 后
        if (Objects.nonNull(userId) && userId != 0L) {
            prefix.append("_").append(userId);
        }
        // 如果存在公司也存在id 前端
        if (companyId != null) {
            prefix.insert(0, companyId + "_");
        }
        return prefix.toString();
    }

    /**
     * 根据文件的 MIME 类型判断文件类型并返回相应的文件后缀名。
     *
     * @param file 文件
     * @return String
     */
    public static String getFileExtension(MultipartFile file) {
        String contentType = file.getContentType();
        return getFileExtensionFromMimeType(contentType);
        // 无法获取到内容类型信息
    }

    public static String getFileExtensionFromMimeType(String contentType) {
        if (contentType != null) {
            if (contentType.startsWith("image/jpeg")) {
                return "jpg";
            } else if (contentType.startsWith("image/png")) {
                return "png";
            } else if (contentType.startsWith("image/gif")) {
                return "gif";
            } else if (contentType.startsWith("image/bmp")) {
                return "bmp";
            } else if (contentType.startsWith("image/webp")) {
                return "webp";
            } else if (contentType.startsWith("audio/mpeg")) {
                return "mp3";
            } else if (contentType.startsWith("audio/wav")) {
                return "wav";
            } else if (contentType.startsWith("audio/ogg")) {
                return "ogg";
            } else if (contentType.startsWith("video/mp4")) {
                return "mp4";
            } else if (contentType.startsWith("video/webm")) {
                return "webm";
            } else if (contentType.startsWith("video/x-msvideo")) {
                return "avi";
            } else if (contentType.startsWith("application/pdf")) {
                return "pdf";
            } else if (contentType.startsWith("application/msword")) {
                return "doc";
            } else if (contentType.startsWith("application/vnd.ms-excel")) {
                return "xls";
            } else if (contentType.startsWith("application/vnd.ms-powerpoint")) {
                return "ppt";
            } else if (contentType.startsWith("application/zip")) {
                return "zip";
            } else if (contentType.startsWith("application/gzip")) {
                return "gz";
            } else if (contentType.startsWith("application/x-tar")) {
                return "tar";
            } else if (contentType.startsWith("text/plain")) {
                return "txt";
            } else if (contentType.startsWith("text/html")) {
                return "html";
            } else if (contentType.startsWith("text/xml")) {
                return "xml";
            } else {
                // 其他未列举的类型，你可以根据需要进行扩展
                throw new IllegalArgumentException("识别不了上传的文件类型");
            }
        }
        throw new IllegalArgumentException("无法获取到内容类型信息");
    }

    /**
     * 判断值是否为空 否返回自身  是给对应的类型默认值 （注意：obj和o这俩个对象必须是相同类型）
     *
     * @param obj 任意对象
     * @param o   泛型类
     * @return Object
     */
    @SuppressWarnings("unchecked")
    public static <T> T isEmptyDefault(Object obj, Class<T> o) {
        // 用于判断一个类是否可以赋值给另一个类。具体而言，它用于测试两个类之间的关系，检查一个类是否是另一个类的子类或实现了相同的接口，或者两者之间是否存在其他继承关系。
        if (o.isAssignableFrom(BigDecimal.class)) {
            return (T) Optional.ofNullable(obj).orElse(BigDecimal.ZERO);
        }
        if (o.isAssignableFrom(Double.class)) {
            return (T) Optional.ofNullable(obj).orElse(0.0d);
        }
        // 其他类型自行添加
        return (T) obj;
    }

    public static BigDecimal isEmptyDefault(BigDecimal obj) {
        return isEmptyDefault(obj, BigDecimal.class);
    }

    public static Double isEmptyDefault(Double obj) {
        return isEmptyDefault(obj, Double.class);
    }

    /**
     * 判断一个值 是否存在一个数组内（必须是同种类型）
     *
     * @param target 要判断的目标值
     * @param array  判断目标值是否存在的数组
     * @return boolean
     */
    public static boolean contains(Object target, Object... array) {
        Assert.isTrue(array != null && target != null, "入参target或array不能为空!");
        // 判断是否是为同种数据类型
        if (target.getClass().isAssignableFrom(array[0].getClass())) {
            for (Object element : array) {
                if (element == target) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断是否为true 是执行s（函数式接口）
     *
     * @param isTrue 是否为true
     * @param s      生产者
     */
    public static void isTrueExec(boolean isTrue, Supplier<Object> s) {
        if (isTrue) {
            s.get();
        }
    }

    /**
     * 判断是否为true 是执行s（函数式接口）抛出异常
     *
     * @param isTrue 是否为true
     * @param var1   异常类
     * @param <X>    异常类型
     * @throws X 抛出异常
     */
    public static <X extends Throwable> void isThrow(boolean isTrue, Supplier<? extends X> var1) throws X {
        if (isTrue) {
            throw var1.get();
        }
    }

    public static void isTrueExec(boolean isTrue, Consumer<Object> s) {
        if (isTrue) {
            s.accept(true);
        }
    }

    public static void isTrueExec(boolean isTrue, Supplier<Object> s, Supplier<Object> s1) {
        if (isTrue) {
            s.get();
        } else {
            s1.get();
        }
    }

    /**
     * 根据给定对象创建一个Optional实例
     * 如果对象为null，或者为空集合，或者为空字符串等等类型，则返回一个空的Optional
     * 否则返回一个包含该对象的Optional
     *
     * @param obj 任意类型的对象
     * @param <T> 对象的类型
     * @return 包含给定对象的Optional实例，或者空的Optional
     */
    public static <T> Optional<T> of(T obj) {
        // 检查对象是否为null
        if (obj == null) {
            return Optional.empty();
        }
        // 检查对象是否为集合类型，并且集合是否为空
        if (obj instanceof Collection) {
            if (CollectionUtils.isEmpty((Collection<?>) obj)) {
                return Optional.empty();
            }
        }
        // 检查对象是否为字符串类型，并且字符串是否为空白
        if (obj instanceof String) {
            if (StringUtils.isBlank((String) obj)) {
                return Optional.empty();
            }
        }
        if (obj instanceof Integer) {
            if ((Integer) obj == 0) {
                return Optional.empty();
            }
        }
        if (obj instanceof Long) {
            if ((Long) obj == 0) {
                return Optional.empty();
            }
        }
        if (obj instanceof Object[]) {
            if (((Object[]) obj).length == 0) {
                return Optional.empty();
            }
        }
        // 对象非空，返回包含该对象的Optional
        return Optional.of(obj);
    }

    /**
     * 使用 groupingBy 对 某个字段值 进行分组并统计出现次数
     *
     * @param list    判断列表
     * @param fun     函数式接口（获取计划集合中的某个对象的字段值）
     * @param message 抛出异常的提示信息
     */
    public static <T> void checkForDuplication(List<? extends T> list, Function<? super T, ?> fun, String message) {
        if (CollectionUtils.isNotEmpty(list)) {
            Set<?> duplicate = list.stream()
                    .map(fun)
                    .filter(Objects::nonNull)  // 排除 null
                    .filter(value -> !(value instanceof String) || StringUtils.isNotBlank((String) value)) // 排除空字符串
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) // 统计每个 对象中字段变量值 出现的次数
                    .entrySet().stream()
                    .filter(e -> e.getValue() > 1)  // 筛选出出现次数大于1
                    .map(Map.Entry::getKey) // 获取重复的变量值
                    .collect(Collectors.toSet());
            Assert.isTrue(duplicate.size() == 0, message + ":" + duplicate);
        }
    }


    /**
     * 根据身份证获取生日日期
     *
     * @param idCard 身份证号码
     * @return Date
     */
    public static Date extractBirthDate(String idCard) {
        try {
            // 检查身份证号码是否有效
            if (StringUtils.isNotBlank(idCard) && idCard.length() == 18) {
                // 提取出生日期部分
                String birthDate = idCard.substring(6, 14);
                // 格式化日期
                return DateUtils.parseDate(birthDate.substring(0, 4) + "-" + birthDate.substring(4, 6) + "-" + birthDate.substring(6, 8), "yyyy-MM-dd");
            }
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
        return null; // 无效身份证号
    }

    /**
     * 通过当前时间戳和随机数组合生成唯一文件名，便于根据时间排序。
     *
     * @return String
     */
    public static String buildFileName(MultipartFile f) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String timestamp = sdf.format(new Date());
        // 生成 0-99999 的随机数
        int randomNum = new Random().nextInt(100000);
        return timestamp + "_" + randomNum + "." + getFileExtension(f);
    }


    public static void main(String[] args) {

        System.out.println(extractBirthDate("44"));
        System.exit(0);
        List<Integer> strings = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 11, 12, 13, 13, 13, 16, 17, 18, 19, 20);
        checkForDuplication(strings, x -> x, "重复");


    }

}
